﻿using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using AOT.Game.ID;
using Net.Msg;
using Net.Public.EventSystem;
using Net.Public.Helper;
using Net.Public.Msg;

namespace Net.Public.Server
{
    public class WServer
    {
        private IdPool _idPool;
        private Socket _server;
        public ConcurrentDictionary<int, ClientSockt> clientDic;
        private ConcurrentQueue<BaseMsg> _msgQueue;
        private MsgHandler _msgHandler;
        private bool isOpen;
        public int ClientNum => clientDic != null ? clientDic.Count : 0;
        private string ip;
        private int port;
        private int maxConnectNum;

        public WServer(string ip, int port, int maxConnectNum)
        {
            this.ip = ip;
            this.port = port;
            this.maxConnectNum = maxConnectNum;
            _msgHandler = new MsgHandler();
            _idPool = new IdPool(0, 100);
        }

        public void Start()
        {
            BeginListen();
            // 1.开启接受客户端线程
            ThreadPool.QueueUserWorkItem(ReceiveClient);
            // 2.开启接受客户端信息线程
            ThreadPool.QueueUserWorkItem(ReceiveMsg);
            // 3.开启处理信息的线程
            ThreadPool.QueueUserWorkItem(HandleMsg);
        }


        public void SendMsgToClient(int clientId, BaseMsg msg)
        {
            clientDic[clientId].SendMsgToClient(msg);
        }

        public void BroadcastMsgToClients(BaseMsg msg)
        {
            foreach (var client in clientDic.Values)
            {
                client.SendMsgToClient(msg);
            }
        }

        public void RemoveClient(int id)
        {
            if (!clientDic.ContainsKey(id))
            {
                Debugger.Log("不包含这个客户端，服务器不能移除");
                return;
            }

            if (!clientDic.TryRemove(id, out ClientSockt client))
            {
                Debugger.Log($"移除客户端失败:{id}");
            }
            else
            {
                client.Close();
            }
        }

        private void RemoveAllClient()
        {
            foreach (var client in clientDic.Values)
            {
                client.Close();
            }

            clientDic.Clear();
            clientDic = null;
        }

        public void Close(bool reuseSocket = true)
        {
            this.isOpen = false;
            if (this._server.Connected)
            {
                this._server.Disconnect(reuseSocket);
                this._server.Shutdown(SocketShutdown.Both);
            }
        }

        public void Dispose()
        {
            if(this._server == null) return;
            Close();
            this._server.Close();
            RemoveAllClient();
            clientDic?.Clear();
            clientDic = null;
            _msgQueue?.Clear();
            _msgQueue = null;
            _msgHandler.Close();
            _msgHandler = null;
        }

        #region 内部实现

        private void HandleMsg(object? state)
        {
            while (isOpen)
            {
                while (_msgQueue.TryDequeue(out BaseMsg msg))
                {
                    RpcEventSystem.Instance.TriggerMsg(msg);
                }
            }
        }

        private void BeginListen()
        {
            _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            _server.Bind(ipEndPoint);
            _server.Listen(this.maxConnectNum);
            clientDic = new ConcurrentDictionary<int, ClientSockt>();
            _msgQueue = new();
            isOpen = true;
        }

        private void ReceiveClient(object? state)
        {
            while (isOpen)
            {
                try
                {
                    var clientSocket = _server.Accept();
                    int id = _idPool.GetId();
                    ClientSockt client = new ClientSockt(clientSocket);
                    clientDic.TryAdd(id, client);
                    LoginMsg loginMsg = new LoginMsg();
                    loginMsg.ClientId = id;
                    BaseMsg msg = new BaseMsg(loginMsg, EMsgType.LoginMsg);
                    SendMsgToClient(id,msg);
                }
                catch (Exception e)
                {
                    Close();
                    throw new Exception("服务器接收客户端连接出现问题");
                }
            }
        }

        private void ReceiveMsg(object? state)
        {
            while (isOpen)
            {
                foreach (var client in clientDic.Values)
                {
                    if (client.IsConnect)
                    {
                        var msg = client.ReceiveMsgFromClient();
                        if (msg.Item2)
                        {
                            _msgQueue.Enqueue(msg.Item1);
                        }
                    }
                }
            }
        }

        #endregion
    }
}